package com.example.demo004springcache.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * 自定义redis序列化配置类
 */
@Configuration
//开启SpringCaching的支持，底层自动识别相关SpringCache的注解
@EnableCaching
public class RedisCacheConfig {

    /**
     * 配置 cacheManager 代替默认的cacheManager （缓存管理器）
     * 当前使用的redis缓存做为底层实现，如果将来想替换缓存方案，那么只需调整CacheManager的实现细节即可
     * 其他代码无需改动
     *
     * @param factory RedisConnectionFactory
     * @return CacheManager
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        //jackson序列化方式对象
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        // 激活默认类型处理，确保在序列化和反序列化过程中能够正确处理子类类型，以至于不会发生类型转换。
        // 参数解释：
        // LaissezFaireSubTypeValidator.instance：这是一个验证器实例，用于验证子类类型。它允许在没有显式类型信息的情况下进行类型处理。
        // ObjectMapper.DefaultTyping.EVERYTHING：这个枚举值表示对所有类型启用默认类型处理，确保在序列化和反序列化过程中能够正确处理各种类型的对象。
        // JsonTypeInfo.As.PROPERTY：这个枚举值表示将类型信息作为属性存储在 JSON 中，以便在反序列化时能够正确地恢复对象的类型。
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.EVERYTHING, JsonTypeInfo.As.PROPERTY);

        serializer.setObjectMapper(objectMapper);
        // 配置key value序列化
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
                //修改前缀与key的间隔符号，默认是::  eg:name:findById
                .computePrefixWith(prefix -> prefix + ":");

        //设置特有的Redis配置
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        //定制化的Cache 设置过期时间 eg:以user::开头的缓存存活时间为20s
        cacheConfigurations.put("user", customRedisCacheConfiguration(config, Duration.ofSeconds(20)));
        //构建redis缓存管理器
        return RedisCacheManager.builder(factory)
                //Cache事务支持,保证redis下的缓存与数据库下的数据一致性
                .transactionAware()
                .withInitialCacheConfigurations(cacheConfigurations)
                .cacheDefaults(config)
                .build();
    }

    /**
     * 设置RedisConfiguration配置
     */
    public RedisCacheConfiguration customRedisCacheConfiguration(RedisCacheConfiguration config, Duration ttl) {
        //设置缓存超时时间
        return config.entryTtl(ttl);
    }
}
